import {
  Scene,
  AxesHelper,
  AmbientLight,
  PerspectiveCamera,
  WebGLRenderer,
  Vector2,
  Mesh,
  Line,
  Cache,
  Texture,
  Raycaster,
  Color
} from "three";
import { CSS2DRenderer } from "three/examples/jsm/renderers/CSS2DRenderer.js";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";

export class InitThree {
  constructor(container) {
    this.container = container;
    this.init();
  }

  init() {
    this.initScene();
    // this.addHelpers();
    this.initLight();
    this.initCamera();
    this.initRenderer();
    this.initController();
    this.addEvents();
  }

  // 场景
  initScene() {
    this.scene = new Scene();
    // this.scene.background = new Color("#000000");
  }

  // 添加辅助线
  addHelpers() {
    const axesHelper = new AxesHelper(5000);
    this.scene.add(axesHelper);
  }

  // 光照
  initLight() {
    this.ambientLight = new AmbientLight(0xffffff, 1);
    this.scene.add(this.ambientLight);
  }

  // 相机
  initCamera() {
    this.camera = new PerspectiveCamera(
      75,
      window.innerWidth / window.innerHeight,
      0.1,
      100000
    );
    this.camera.position.set(0, 15, 25);
    this.camera.lookAt(this.scene.position);
  }

  // 渲染器
  initRenderer() {
    this.renderer = new WebGLRenderer({ antialias: true, alpha: true });
    // this.renderer.setClearColor(0xffffff, 0);
    this.renderer.setClearColor(new Color('#32373E'), 1);
   
    this.renderer.setSize(
      this.container.offsetWidth,
      this.container.offsetHeight
    );
    this.renderer.setPixelRatio(window.devicePixelRatio);
    this.camera.aspect =
      this.container.offsetWidth / this.container.offsetHeight;
    this.camera.updateProjectionMatrix();
    this.container.appendChild(this.renderer.domElement);
  }

  render() {
    this.renderer && this.renderer.render(this.scene, this.camera);
    this.controller && this.controller.update();
  }

  initController() {
    this.controller = new OrbitControls(this.camera, this.renderer.domElement);
    // this.controller.minDistance = 0.6;
    // this.controller.maxDistance = 1.2;
    // this.controller.minPolarAngle = Math.PI / 2;
    // this.controller.maxPolarAngle = Math.PI / 1.2;
    // this.controller.minAzimuthAngle = -Math.PI / 6;
    // this.controller.maxAzimuthAngle = Math.PI / 6;
    // this.controller.enablePan = false;
  }

  initCss2dRender() {
    const css2dRenderer = new CSS2DRenderer();
    css2dRenderer.setSize(
      this.container.offsetWidth,
      this.container.offsetHeight
    );
    css2dRenderer.domElement.setAttribute("class", "css2d-label");
    css2dRenderer.domElement.style.position = "absolute";
    css2dRenderer.domElement.style.top = "0px";
    css2dRenderer.domElement.style.pointerEvents = "none";
    this.container.appendChild(css2dRenderer.domElement);
    return css2dRenderer;
  }

  addEvents() {
    window.addEventListener("resize", this.resize.bind(this), false);
  }

  resize() {
    if (this.renderer) {
      this.renderer.setSize(
        this.container.offsetWidth,
        this.container.offsetHeight
      );
      this.camera.aspect =
        this.container.offsetWidth / this.container.offsetHeight;
      this.camera.updateProjectionMatrix();
      this.css2dRenderer &&
        this.css2dRenderer.setSize(
          this.container.offsetWidth,
          this.container.offsetHeight
        );
    }
  }

  resetUVs(geometry) {
    geometry.computeBoundingBox();
    const max = geometry.boundingBox.max;
    const min = geometry.boundingBox.min;
    const offset = new Vector2(0 - min.x / 2, 0 - min.y);
    const range = new Vector2(max.x - min.x, max.y - min.y);

    const uvAttribute = geometry.attributes.uv;

    for (let i = 0; i < uvAttribute.count; i++) {
      let u = uvAttribute.getX(i);
      let v = uvAttribute.getY(i);
      let z = uvAttribute.getZ(i);

      u = (u + offset.x) / range.x;
      v = (v + offset.y) / range.y;
      z = (z + offset.y) / range.y;
      uvAttribute.setXYZ(i, u, v, z);
    }
    uvAttribute.needsUpdate = true;
  }

  clickEvent() {
    this.mouse = new Vector2();
    this.raycaster = new Raycaster();
    document.addEventListener(
      "click",
      (event) => {
        event.preventDefault();
        this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
        this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
        this.renderEvent();
      },
      false
    );
  }

  renderEvent() {
    this.raycaster.setFromCamera(this.mouse, this.camera);

    this.intersects = this.raycaster.intersectObjects(this.scene.children);
    let intersected;
    if (this.intersects.length > 0) {
      const obj = this.intersects[0].object;
      if (!obj.name.includes("clickable")) return;
      if (intersected != this.intersects[0].object) {
        if (intersected) {
          intersected.material.emissive.setHex(intersected.currentHex);
        }

        intersected = obj;
        intersected.currentHex = intersected.material.emissive.getHex();
        intersected.material.emissive.setHex(0xff0000);
      }
    } else {
      if (intersected)
        intersected.material.emissive.setHex(intersected.currentHex);

      intersected = null;
    }
  }

  destroy() {
    window.removeEventListener("resize", this.resize);
    this.dispose(this.scene, this.scene);
    this.renderer.dispose();
    this.renderer.renderLists.dispose();
    this.renderer.forceContextLoss();
    this.renderer.domElement = null;
    this.controller = null;
    this.container.innerHTML = null;
    Cache.clear();
    this.scene.clear();
    this.scene = null;
    this.renderer = null;
    this.camera = null;
  }

  dispose(parent, child) {
    if (child.children.length > 0) {
      let arr = child.children.filter((x) => x);
      arr.forEach((a) => {
        this.dispose.call(this, child, a);
      });
    }
    if (child instanceof Mesh || child instanceof Line) {
      if (child.material.map && child.material.map instanceof Texture) {
        child.material.map.dispose();
      }
      if (child.material) {
        if (Array.isArray(child.material)) {
          child.material.forEach((item) => {
            item.dispose();
          });
          return;
        }
        child.material.dispose();
      }
      child.geometry && child.geometry.dispose();
    } else if (child.material) {
      child.material && child.material.dispose();
    }
    child.remove();
    parent.remove(child);
  }
}
